//
// This file provide various ways that you could make use of this txn memory manager to allocate memory
// By override the standard new / delete operator, of course, you could provide more override  version
// with more arguments

#pragma once
#include <xmemory>
#include "HeapMgr.h"

// 1. Utility functions for operator new / delete on txn heap
namespace TxnMemUtil
{
	inline void* operator new (size_t size)
	{
		return HeapMgr::Get()->AllocStorage(size);
	}
	inline void operator delete(void* p)
	{
		HeapMgr::Get()->FreeStorage(p);
	}
	inline void* operator new [] (size_t size)
	{
		return TxnMemUtil::operator new(size);
	}
	inline void operator delete [] (void *p)
	{
		TxnMemUtil::operator delete(p);
	}
}

// 2. Base class  for operator new / delete on txn heap, each class derived from this class
// will allocate memory on the txn heap
class TxnObect
{
public:
	//static new_handler set_new_handler(new_handler p);
	inline void* operator new (size_t size)
	{
		return TxnMemUtil::operator new(size);
	}
	inline void operator delete(void* p)
	{
		return TxnMemUtil::operator delete(p);
	}
	inline void* operator new [] (size_t size)
	{
		return TxnMemUtil::operator new [](size);
	}
	inline void operator delete [] (void *p)
	{
		TxnMemUtil::operator delete [](p);
	}
};



// 2. std allocator on txn heap
template<class T>
class txn_allocator : public std::allocator<T>
{
public:
	~txn_allocator() {}

	typedef value_type _FARQ *pointer;
	typedef value_type _FARQ& reference;
	typedef const value_type _FARQ *const_pointer;
	typedef const value_type _FARQ& const_reference;

	typedef _SIZT size_type;
	typedef _PDFT difference_type;

	template<class _Other>
	struct rebind
	{	// convert an allocator<T> to an allocator <_Other>
		typedef txn_allocator<_Other> other;
	};

	pointer address(reference _Val) const
	{	// return address of mutable _Val
		return (&_Val);
	}

	const_pointer address(const_reference _Val) const
	{	// return address of nonmutable _Val
		return (&_Val);
	}

	txn_allocator()
	{	// construct default allocator (do nothing)
	}

	txn_allocator(const txn_allocator<T>&)
	{	// construct by copying (do nothing)
	}

	template<class _Other>
	txn_allocator(const txn_allocator<_Other>&)
	{	// construct from a related allocator (do nothing)
	}

	template<class _Other>
	txn_allocator<T>& operator=(const txn_allocator<_Other>&)
	{	// assign from a related allocator (do nothing)
		return (*this);
	}

	void deallocate(pointer _Ptr, size_type)
	{	// deallocate object at _Ptr, ignore size
		HeapMgr* pHeapMgr = HeapMgr::Get();
		if(pHeapMgr->IsStorage(_Ptr))
		{
			(*_Ptr).~T(); 
			pHeapMgr->FreeStorage(_Ptr); 
		}
	}

	pointer allocate(size_type _Count)
	{	
		// allocate storage for _Count elements of type T
		return (T _FARQ *)HeapMgr::Get()->AllocStorage(_Count * sizeof (T));
	}

	pointer allocate(size_type _Count, const void _FARQ *)
	{	// allocate array of _Count elements, ignore hint
		return (allocate(_Count));
	}

	void construct(pointer _Ptr, const T& _Val)
	{	
		// construct object at _Ptr with value _Val
		new ((void _FARQ *)_Ptr) T(_Val);
	}

	void destroy(pointer _Ptr)
	{	
		// destroy object at _Ptr
		// _Ptr->~T();
		(*_Ptr).~T(); 
	}

	_SIZT max_size() const
	{	// estimate maximum array size
		_SIZT _Count = (_SIZT)(-1) / sizeof (T);
		return (0 < _Count ? _Count : 1);
	}
};


// 3. Customized new operator use placement new
template <class T>
T* txn_new() { return new (HeapMgr::Get()->AllocStorage(sizeof(T))) T(); }

template <class T>
void txn_delete(T* p) { p->~T(); HeapMgr::Get()->FreeStorage(p); }


// 4. Override global new operator
// You would be better not override the global operator new: 
//		a. you don't have the chance to allocate memory on crt heap
//		b. compatibility problem with 3rd parties libraries
//
//void* operator new (size_t size);
//void operator delete (void *p);
//void* operator new [] (size_t size);
//void operator delete [] (void *p);
